/******************************************************************************* * Copyright (c) 2000, 2015 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.ui.internal.decorators; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.eclipse.core.runtime.IExtension; import org.eclipse.core.runtime.ISafeRunnable; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.SafeRunner; import org.eclipse.core.runtime.dynamichelpers.IExtensionTracker; import org.eclipse.osgi.util.NLS; import org.eclipse.ui.internal.ObjectContributorManager; import org.eclipse.ui.internal.WorkbenchMessages; import org.eclipse.ui.internal.WorkbenchPlugin; import org.eclipse.ui.internal.misc.StatusUtil; import org.eclipse.ui.internal.util.Util; /** * The LightweightDecoratorManager is a decorator manager that encapsulates the * behavior for the lightweight decorators. */ public class LightweightDecoratorManager extends ObjectContributorManager { /** * The runnable is the object used to run the decorations so that an error * in someones decorator will not kill the thread. It is implemented here to * prevent aborting of decoration i.e. successful decorations will still be * applied. */ private static class LightweightRunnable implements ISafeRunnable { static class RunnableData { final DecorationBuilder builder; final LightweightDecoratorDefinition decorator; final Object element; public RunnableData(Object object, DecorationBuilder builder, LightweightDecoratorDefinition definition) { this.element = object; this.builder = builder; this.decorator = definition; } boolean isConsistent() { return builder != null && decorator != null && element != null; } } private volatile RunnableData data = new RunnableData(null, null, null); void setValues(Object object, DecorationBuilder builder, LightweightDecoratorDefinition definition) { data = new RunnableData(object, builder, definition); } /* * @see ISafeRunnable.handleException(Throwable). */ @Override public void handleException(Throwable exception) { IStatus status = StatusUtil.newStatus(IStatus.ERROR, exception .getMessage(), exception); LightweightDecoratorDefinition decorator = data.decorator; String message; if (decorator == null) { message = WorkbenchMessages.DecoratorError; } else { String name = decorator.getName(); if (name == null) { // decorator definition is not accessible anymore name = decorator.getId(); } message = NLS.bind(WorkbenchMessages.DecoratorWillBeDisabled, name); } WorkbenchPlugin.log(message, status); if (decorator != null) { decorator.crashDisable(); } clearReferences(); } /* * @see ISafeRunnable.run */ @Override public void run() throws Exception { // Copy to local variables, see // https://bugs.eclipse.org/bugs/show_bug.cgi?id=300358 RunnableData data = this.data; if (data.isConsistent()) { data.decorator.decorate(data.element, data.builder); } clearReferences(); } /** * Clear all of the references in the receiver. * * @since 3.1 */ void clearReferences() { data = new RunnableData(null, null, null); } } private LightweightRunnable runnable = new LightweightRunnable(); // The lightweight definitions read from the registry private LightweightDecoratorDefinition[] lightweightDefinitions; private static final LightweightDecoratorDefinition[] EMPTY_LIGHTWEIGHT_DEF = new LightweightDecoratorDefinition[0]; LightweightDecoratorManager(LightweightDecoratorDefinition[] definitions) { super(); lightweightDefinitions = definitions; buildContributors(); } /** * Get the lightweight definitions for the receiver. * * @return LightweightDecoratorDefinition[] */ LightweightDecoratorDefinition[] getDefinitions() { return lightweightDefinitions; } /** * Register the decorators as object contributions so that adaptable lookup * can occur. */ private void buildContributors() { for (LightweightDecoratorDefinition decorator : lightweightDefinitions) { for (String type : getTargetTypes(decorator)) { registerContributor(decorator, type); } } } /** * For dynamic UI * * @param decorator * the definition to add * @return whether the definition was added * @since 3.0 */ public boolean addDecorator(LightweightDecoratorDefinition decorator) { if (getLightweightDecoratorDefinition(decorator.getId()) == null) { LightweightDecoratorDefinition[] oldDefs = lightweightDefinitions; lightweightDefinitions = new LightweightDecoratorDefinition[lightweightDefinitions.length + 1]; System.arraycopy(oldDefs, 0, lightweightDefinitions, 0, oldDefs.length); lightweightDefinitions[oldDefs.length] = decorator; // no reset - handled in the DecoratorManager String[] types = getTargetTypes(decorator); for (String type : types) { registerContributor(decorator, type); } return true; } return false; } /** * Get the name of the types that a decorator is registered for. * * @param decorator * @return String[] */ private String[] getTargetTypes(LightweightDecoratorDefinition decorator) { return decorator.getObjectClasses(); } /** * For dynamic-ui * * @param decorator * the definition to remove * @return whether the definition was removed * @since 3.1 */ public boolean removeDecorator(LightweightDecoratorDefinition decorator) { int idx = getLightweightDecoratorDefinitionIdx(decorator.getId()); if (idx != -1) { LightweightDecoratorDefinition[] oldDefs = lightweightDefinitions; Util.arrayCopyWithRemoval( oldDefs, lightweightDefinitions = new LightweightDecoratorDefinition[lightweightDefinitions.length - 1], idx); // no reset - handled in the DecoratorManager for (String type : getTargetTypes(decorator)) { unregisterContributor(decorator, type); } return true; } return false; } /** * Get the LightweightDecoratorDefinition with the supplied id * * @return LightweightDecoratorDefinition or <code>null</code> if it is * not found * @param decoratorId * String * @since 3.0 */ private LightweightDecoratorDefinition getLightweightDecoratorDefinition( String decoratorId) { int idx = getLightweightDecoratorDefinitionIdx(decoratorId); if (idx != -1) { return lightweightDefinitions[idx]; } return null; } /** * Return the index of the definition in the array. * * @param decoratorId * the id * @return the index of the definition in the array or <code>-1</code> * @since 3.1 */ private int getLightweightDecoratorDefinitionIdx(String decoratorId) { for (int i = 0; i < lightweightDefinitions.length; i++) { if (lightweightDefinitions[i].getId().equals(decoratorId)) { return i; } } return -1; } /** * Return the enabled lightweight decorator definitions. * * @return LightweightDecoratorDefinition[] */ LightweightDecoratorDefinition[] enabledDefinitions() { ArrayList result = new ArrayList(); for (LightweightDecoratorDefinition lightweightDefinition : lightweightDefinitions) { if (lightweightDefinition.isEnabled()) { result.add(lightweightDefinition); } } LightweightDecoratorDefinition[] returnArray = new LightweightDecoratorDefinition[result .size()]; result.toArray(returnArray); return returnArray; } /** * Return whether there are enabled lightwieght decorators * * @return boolean */ boolean hasEnabledDefinitions() { for (LightweightDecoratorDefinition lightweightDefinition : lightweightDefinitions) { if (lightweightDefinition.isEnabled()) { return true; } } return false; } /** * Reset any cached values. */ void reset() { runnable.clearReferences(); } /** * Shutdown the decorator manager by disabling all of the decorators so that * dispose() will be called on them. */ void shutdown() { // Disable all fo the enabled decorators // so as to force a dispose of thier decorators for (LightweightDecoratorDefinition lightweightDefinition : lightweightDefinitions) { if (lightweightDefinition.isEnabled()) { lightweightDefinition.setEnabled(false); } } } /** * Get the LightweightDecoratorDefinition with the supplied id * * @return LightweightDecoratorDefinition or <code>null</code> if it is * not found * @param decoratorId * String */ LightweightDecoratorDefinition getDecoratorDefinition(String decoratorId) { for (LightweightDecoratorDefinition lightweightDefinition : lightweightDefinitions) { if (lightweightDefinition.getId().equals(decoratorId)) { return lightweightDefinition; } } return null; } /** * Get the lightweight registered for elements of this type. */ LightweightDecoratorDefinition[] getDecoratorsFor(Object element) { if (element == null) { return EMPTY_LIGHTWEIGHT_DEF; } List elements = new ArrayList(1); elements.add(element); LightweightDecoratorDefinition[] decoratorArray = EMPTY_LIGHTWEIGHT_DEF; List contributors = getContributors(elements); if (!contributors.isEmpty()) { Collection decorators = DecoratorManager.getDecoratorsFor(element, (DecoratorDefinition[]) contributors .toArray(new DecoratorDefinition[contributors .size()])); if (decorators.size() > 0) { decoratorArray = new LightweightDecoratorDefinition[decorators .size()]; decorators.toArray(decoratorArray); } } return decoratorArray; } /** * Fill the decoration with all of the results of the decorators. * * @param element * The source element * @param decoration * The DecorationResult we are working on. where adaptable is * true. */ public void getDecorations(Object element, DecorationBuilder decoration) { for (LightweightDecoratorDefinition decorator : getDecoratorsFor(element)) { decoration.setCurrentDefinition(decorator); decorate(element, decoration, decorator); } } /** * Decorate the element receiver in a SafeRunnable. * * @param element * The Object to be decorated * @param decoration * The object building decorations. * @param decorator * The decorator being applied. */ private void decorate(Object element, DecorationBuilder decoration, LightweightDecoratorDefinition decorator) { runnable.setValues(element, decoration, decorator); SafeRunner.run(runnable); } /** * Method for use by test cases * * @param object * the object to be decorated * @return the decoration result */ public DecorationResult getDecorationResult(Object object) { DecorationBuilder builder = new DecorationBuilder(); getDecorations(object, builder); return builder.createResult(); } @Override public void addExtension(IExtensionTracker tracker, IExtension extension) { // Do nothing as this is handled by the DecoratorManager // This is not called as canHandleExtensionTracking returns // false. } }